#include "datamemoryorganisation.hpp"

DataMemoryOrganisation::DataMemoryOrganisation(QObject *parent) :
    QObject(parent)
{
    actualBank = false;
    bank0 = new Bank();
    bank1 = new Bank();
    initRegister();
    workingRegister = new Register(-1, -1);
    connect(workingRegister, SIGNAL(contentChanged(int)), this, SLOT(onContentChanged(int)),Qt::UniqueConnection);
    connect(portB, SIGNAL(contentChanged(int)), this, SLOT(onContentChanged(int)));
    prescaler = new Register(-2, -1);
}


DataMemoryOrganisation::DataMemoryOrganisation(const DataMemoryOrganisation &other)
{
    this->actualBank = other.getActualBank();
    this->workingRegister = other.getWorkingRegister();
    this->prescaler = other.prescaler;

    this->bank0 = new Bank(*other.getBank(false));
    this->bank1 = new Bank(*other.getBank(true));
}

void DataMemoryOrganisation::switchBank()
{
    if (this->actualBank)
	this->getRegister(3)->clearBit(5);
    else
	this->getRegister(3)->setBit(5);
}

void DataMemoryOrganisation::switchBank(bool bank)
{
    if (bank == this->actualBank)
	return;

    if (bank)
	this->getRegister(3)->clearBit(5);
    else
	this->getRegister(3)->setBit(5);
}

Bank *DataMemoryOrganisation::getActualBank() const
{
    if(!actualBank) {
	return bank0;
    }
    return bank1;
}

Bank *DataMemoryOrganisation::getBank(bool bank) const
{
    if (bank)
	return this->bank1;
    else
	return this->bank0;
}

void DataMemoryOrganisation::initRegister()
{
    //Initiate "special" registers
    int i = 0;
    //Indirect addr.(1) 0x00
    Register *indirectAddress = new Register(i, 2);
    bank0->addRegister(i, indirectAddress);
    bank1->addRegister(i, indirectAddress);
    i++;

    //TMR0/OPTION_REG 0x01
    bank0->addRegister(i, new Register(i, 0, 255));
    bank1->addRegister(i, new Register(i, 1));
    i++;

    //PCL 0x02
    Register *pcl = new Register(i, 2);
    bank0->addRegister(i, pcl);
    bank1->addRegister(i, pcl);
    i++;

    //STATUS 0x03
    Register *status = new Register(i, 2, 24);
    bank0->addRegister(i, status);
    bank1->addRegister(i, status);
    i++;

    //FSR 0x04
    Register *fsr = new Register(i, 2);
    bank0->addRegister(i, fsr);
    bank1->addRegister(i, fsr);
    i++;

    //PORTA/TRISA 0x05
    this->portA = new IORegister(i, 0, 0);
    bank0->addRegister(i, this->portA);
    Register *trisA = new Register(i, 1, 31);
    bank1->addRegister(i, trisA);
    this->portA->setTris(trisA);
    i++;

    //PORTB/TRISB 0x06
    this->portB = new IORegister(i, 0, 0);
    bank0->addRegister(i, this->portB);
    Register *trisB = new Register(i, 1, 255);
    bank1->addRegister(i, trisB);
    this->portB->setTris(trisB);
    i++;

    //Not used 0x07
    bank0->addRegister(i, new Register(i, 0));
    bank1->addRegister(i, new Register(i, 1));
    i++;

    //EEDATA/EECON1 0x08
    bank0->addRegister(i, new Register(i, 0));
    bank1->addRegister(i, new Register(i, 1));
    i++;

    //EEADR/EECON2(1) 0x09
    bank0->addRegister(i, new Register(i, 0));
    bank1->addRegister(i, new Register(i, 1));
    i++;

    //PCLATH 0x0A
    Register *pclath = new Register(i, 2);
    bank0->addRegister(i, pclath);
    bank1->addRegister(i, pclath);
    i++;

    //INTCON 0x0B
    Register *intcon = new Register(i, 2);
    bank0->addRegister(i, intcon);
    bank1->addRegister(i, intcon);
    i++;

    //Initiate "normal" registers start at  0x0C
    for(; i <= 0x4F; i++)
    {
	Register *tempRegister = new Register(i, 2);
	bank0->addRegister(i, tempRegister);
	bank1->addRegister(i, tempRegister);
    }

    //Connect Registersignals with on_register_changed-slot
    for(i = 1; i <= 0x4F; i++)
    {
	connect(bank0->getRegister(i), SIGNAL(contentChanged(int)), this, SLOT(onContentChanged(int)),Qt::UniqueConnection);
	connect(bank1->getRegister(i), SIGNAL(contentChanged(int)), this, SLOT(onContentChanged(int)),Qt::UniqueConnection);
    }
}

IORegister *DataMemoryOrganisation::getPortA()
{
    return portA;
}

IORegister *DataMemoryOrganisation::getPortB()
{
    return portB;
}

Register *DataMemoryOrganisation::getWorkingRegister() const
{
    return workingRegister;
}

Register *DataMemoryOrganisation::getPrescaler()
{
    return prescaler;
}


void DataMemoryOrganisation::onContentChanged(int oldValue)
{
    Register *reg = static_cast<Register*>(QObject::sender());
    emit registerChanged(reg);

    // Working Register
    if(reg->getAddress() == -1 && reg->getBank() == -1)
    {
	emit registerChanged(getWorkingRegister());
	return;
    }

    // Check if specific Bank has been set and switch if needed
    if (reg->getBank() > -1 && reg->getBank() < 2)
	switchBank(reg->getBank() == 1);

    // Status Register
    if(reg->getAddress() == 3)
    {
	this->actualBank = reg->isSet(5);
	emit switchBankSignal(this->actualBank);
    }

    //Port B Register
    if(reg->getAddress() == 0x6)
    {
	Register *intcon = this->getActualBank()->getRegister(0xB);
	Register *options = this->getRegister(0x81);

	//XOR RB0 and INTEDG if value is 0 interrupt is thrown
	if(!portB->isSet(0) ^ options->isSet(6))
	{
	    intcon->setBit(1);
	}
	else
	{
	    intcon->clearBit(1);
	}

	oldValue = oldValue & 0b11110000;
	int newValue = portB->getValue() & 0b11110000;
	if(oldValue != newValue)
	{
	    intcon->setBit(0);
	}
	else
	{
	    intcon->clearBit(0);
	}

	//Global Interrupt
	if(intcon->isSet(7))
	{
	    if(/*intcon->isSet(5) && intcon->isSet(2) || */intcon->isSet(4) && intcon->isSet(1) || intcon->isSet(3) && intcon->isSet(0))
	    {
		emit interrupt();
	    }
	}
    }
}

void DataMemoryOrganisation::onRequestChangeRegister(int address, int value)
{
    Register *reg = this->getRegister(address);

    //Switch to the correct Bank
    if ((reg->getBank() == 0 && this->actualBank) || (reg->getBank() == 1 && !this->actualBank))
	this->switchBank();

    reg->setValue(value, true);
}


Register* DataMemoryOrganisation::getRegister(int address)
{
    if ((address & 128) > 0)
	return this->bank1->getRegister((address & 127));
    else
	return this->bank0->getRegister((address & 127));
}

void DataMemoryOrganisation::reset()
{
    this->bank1->reset();
    this->bank0->reset();
    this->workingRegister->clear();
}

void DataMemoryOrganisation::resetNormalOperation()
{
    this->getRegister(2)->reset();
    (*this->getRegister(3)) &= 0b00011111;
    this->getRegister(0xA)->reset();
    (*this->getRegister(0xB)) &= 0b00000001;
    this->getRegister(81)->reset();
    this->getRegister(85)->reset();
    this->getRegister(86)->reset();
    (*this->getRegister(88)) &= 0b00001000;
}


void DataMemoryOrganisation::onRequestSwitchBank(bool bank)
{
    if (bank)
	this->getRegister(3)->setBit(5);
    else
	this->getRegister(3)->clearBit(5);
}
